```
=======================================================================
WELCOME BACK TO REGULAR EXPRESSIONS WITH PYTHON'S RE MODULE: LESSON 2
=======================================================================

Great to see you returning for our second lesson on Regular Expressions in Python using the `re` module. We'll build on what you learned in Lesson 1 and introduce some new powerful concepts. 

To follow along, open your terminal and type `ipython` to start the interactive Python shell. Don't forget to import the `re` module again with the command:

```python
import re
```

=======================================================================
CONCEPT 1: ANCHORS - START AND END OF A LINE
=======================================================================

Anchors are special characters that match positions within a string. They don't match any characters but rather positions within a string.

- `^` asserts the start of a line.
- `$` asserts the end of a line.

**Example:** Let's check if a string starts with 'Hello'.

```python
match = re.search('^Hello', 'Hello, world!')
```

Try the above pattern in your ipython shell.

=======================================================================
EXERCISE 1:
=======================================================================

Determine if the following string ends with an exclamation point: 'Goodbye!'.

```python
# Your code here
```

**Expected Outcome:** Make sure your regex confirms the string ends with '!'.

=======================================================================
CONCEPT 2: FIND ALL MATCHES WITH re.findall()
=======================================================================

`re.findall()` returns all non-overlapping matches of a pattern in a string as a list of strings. While `re.search()` finds just one match, `findall` can give you more!

**Example:** Let's find all occurrences of the letter 'o' in a string.

```python
matches = re.findall('o', 'octopus on oxygen')
```

Implement and observe what `matches` contains.

=======================================================================
EXERCISE 2:
=======================================================================

Find all the words that start with the letter 't' in the string: 'the tiger toiled tirelessly'.

```python
# Your code here
```

**Expected Outcome:** You should get each word that begins with 't'.

=======================================================================
CONCEPT 3: GROUPING WITH PARENTHESES
=======================================================================

Parentheses `()` are used for grouping in regex. They capture the matched text as a group, which you can access afterwards.

**Example:** Capture words between whitespace.

```python
match = re.search(r'(\w+) (\w+)', 'Hello World')
```

What do `match.group(1)` and `match.group(2)` return? Try it out!

=======================================================================
EXERCISE 3:
=======================================================================

Extract the area code from the phone number in the format (123) 456-7890.

```python
# Your code here
```

**Expected Outcome:** Your solution should isolate '123' from '(123) 456-7890'.

=======================================================================
CONCEPT 4: OPTIONAL CHARACTERS WITH '?'
=======================================================================

The question mark `?` makes the preceding character optional (it matches zero or one occurrence).

**Example:** Let's match the British or American spelling of 'color'/'colour'.

```python
match = re.search('colou?r', 'color')
```

Test it with the word 'colour' as well.

=======================================================================
EXERCISE 4:
=======================================================================

Create a regex pattern to match the spellings of 'gray' or 'grey'.

```python
# Your code here
```

**Expected Outcome:** Both spellings should match successfully.

=======================================================================
CHALLENGE:
=======================================================================

Using what you've learned, write a regex pattern to extract all valid email addresses from the string: 'Please email john_doe@example.com or jane.doe123@sample-site.org for further assistance.'

```python
# Your code here
```

**Success Criteria:** Your pattern should correctly find both email addresses.

=======================================================================
FURTHER EXPLORATION:
=======================================================================

- Study the usage of `{}` for specifying specific repetition counts.
- Experiment further with `(?P<name>...)` for named groups.
- Look into more advanced topics like lookahead and lookbehind assertions.
- Explore `re.sub()` to replace parts of a string via regex.

Keep pushing those regex boundaries and experimenting! Remember, practice is key to mastering regex. You're doing fantastic, and I look forward to seeing you in Lesson 3!

=======================================================================

```